<?php
/**
 *	YrPHP
 *	
 *	Class to fetch weather data from Yr (www.yr.no)
 *	
 *	@author Hans Petter Eide (hans.petter.eide --at-- gmail.com)
 *  @version 0.11 alpha
 *  
 */
class YrPHP {
	protected $forecastURL;
	protected $filename;
	protected $numDays;
	protected $iconPath;
	protected $cacheTime;
	protected $xml;
	protected $numPeriods;
	protected $windDirectionText;
	protected $flushXML = False;
	/**	Constructor
	 *
	 *	@param $forecastURL	- the URL to where the forecast can be found,
	 *							(e.g. http://www.yr.no/sted/Norge/Møre_og_Romsdal/Norddal/Eidsvatnet/varsel.xml)
	 *	@param $filename 		- the name of the local file to be stored (e.g. varsel.xml)
	 *	@param $numDays		- the number of days to retrieve data from, starting from the current date 
	 *							(0-indexed, so to retrieve data for the next 3 days use 2 as parameter)
	 *	@param $iconPath		- the relative path to where weather icons can be found (e.g icons/weather/)
	 *	@param $cacheTime		- the number of seconds to cache data (default is 3600 seconds == 1 hour)
	 *
	 */

	public function __construct($forecastURL, $filename, $numDays, $iconPath, $cacheTime = 3600){
		$this->forecastURL = $forecastURL;
		$this->filename = $filename;
		$this->numDays = $numDays;
		$this->iconPath = $iconPath;
		$this->cacheTime = $cacheTime;

		// Set names for wind direction
		$this->setWindDirectionNames();
		
		// Create the XML-object	
		$this->setXML();
		
		$this->numPeriods = $this->getNumPeriodsFromNumDays($this->numDays, $this->xml->forecast->tabular->time[0]['period']);
	}
	
	/**
	 * Set forecast URL to use
	 *
	 * @param string $forecastURL
	 */
	public function setForecastURL($forecastURL){
		$this->forecastURL = $forecastURL;
		$this->flushXML = True;
	}
	
	/**
	 * Set filename to use
	 *
	 * @param string $filename
	 */

	public function setFilename($filename){
		$this->filename = $filename;
		$this->flushXML = True;
	}
	

	/**
	 * Set number of days to retreive data from (0-indexed, e.g. first day is 0)
	 * 
	 * @param string $numDays
	 * 
	 */
	public function setNumDays($numDays){
		$this->numDays = $numDays;
		$this->flushXML = True;
	}
	
	/**
	 * Set the path to where the symbol icons can be found
	 * 
	 * @param string $iconPath
	 * 
	 */
	// param $iconPath		- the relative path to where weather icons can be found (e.g icons/weather/)
	public function setIconPath($iconPath){
		$this->iconPath = $iconPath;
		$this->flushXML = True;
	}
	
	/**
	 * Set cache time to use in seconds (recommended value is 3600 seconds == 1 hour)
	 *
	 * @param integer $cacheTime
	 */
	public function setCacheTime($cacheTime){
		$this->cacheTime = $cacheTime;
		$this->flushXML = True;
	}
	
	/**
	 * Set the XML-object to use
	 *
	 */
	public function setXML(){
		if(((time() -  filemtime($this->filename)) > $this->cacheTime) or !file_exists($this->filename) or $this->flushXML){
			if (!function_exists('curl_exec')){
				throw new Exception("CURL library not found. This library must be installed for YrPHP to function");
			}
			$ch = curl_init($this->forecastURL);
			$fp = fopen($this->filename, "w");
			
			curl_setopt($ch, CURLOPT_FILE, $fp);
			curl_setopt($ch, CURLOPT_HEADER, 0);
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
			curl_exec($ch);
			
			if (curl_errno($ch)) {
				throw new Exception("An error occured when trying to retrieve XML-data. Error message: " . curl_error($ch));
			}
			curl_close($ch);
			fclose($fp);
			$this->flushXML = False;
		}
		
		$this->xml = @new SimpleXMLElement(file_get_contents($this->filename));
	}
	
	/**
	 * Returns the number of days set to retrive data
	 *
	 * @return string
	 */
	public function getNumDays(){
		if($this->numDays == ""){
			throw new Exception('$numDays is not defined');
		}
		else{
			return $this->numDays;
		}
	}
	
	/**
	 * Returns the number of periods to retrieve data from
	 *
	 * @return string
	 */
	public function getNumPeriods(){
		if ($this->numPeriods ==""){
			throw new Exception('$numPeriods has no value');
		}
		else{
			return $this->numPeriods;
		}
	}
	
	/**
	 * Returns an array with the period number for each period
	 *
	 * @return array
	 */
	public function getPeriodNumbers(){
		if ($this->numPeriods == ""){
			throw new Exception('Period numbers could not be retrieved since $numPeriods has no value');
		}
		else{
			for($i=0; $i < $this->numPeriods; $i++){
				$periodNumbers[$i] = $this->xml->forecast->tabular->time[$i]['period'];
			}
			return $periodNumbers;
		}
	}
	
	/**
	 * Returns an array with all the from-times based on the number of periods
	 *
	 * @return array
	 */
	public function getTimeFrom(){
		if ($this->numPeriods == ""){
			throw new Exception('Time from could not be retrieved since $numPeriods has no value');
		}
		else {
			for($i=0; $i < $this->numPeriods; $i++){
				$timeFrom[$i] = $this->xml->forecast->tabular->time[$i]['from'];
			}
			return $timeFrom;
		}
	}

	/**
	 * Returns an array with all the to-times based on the number of periods
	 *
	 * @return array
	 */
	public function getTimeTo(){
		if ($this->numPeriods == ""){
			throw new Exception('Time to could not be retrieved since $numPeriods has no value');
		}
		else {
			for($i=0; $i < $this->numPeriods; $i++){
				$timeTo[$i] = $this->xml->forecast->tabular->time[$i]['to'];
			}
			return $timeTo;	
		}
	}
	
	/**
	 *  Returns a relative path to the symbol icons
	 * 
	 * @param integer $id
	 * @param string $fromTime - on the format "HH:MM"
	 * 
	 * @return string
	 */
	public function getSymbolIcon($id, $fromTime){
		if ($fromTime == "00:00"){
			return ($id <= 9 ? $this->iconPath .  "0" . $id . "n.png" : $this->iconPath . $id . "n.png");
		}
		else {
			return ($id <= 9 ? $this->iconPath . "0" . $id . "d.png" : $this->iconPath . $id . "d.png");
		}
	}

	/**
	 * Returns an URL to a wind icon
	 *
	 * @param float $windDirection
	 * @param float $windSpeed
	 * @return string
	 */
	public function getWindIcon($windDirection, $windSpeed){
		return "http://www.yr.no/grafikk/pil.aspx?r=" . $windDirection . "&h=" . $windSpeed;
	}
	
	public function getNumPeriodsFromNumDays($numDays, $firstPeriodVal){
		$firstDayPeriods = 0;

		if ($numDays == ""){
			throw new Exception('Could not get number of periods from number of days since $numDays is not defined');
		}
		if (0 == $firstPeriodVal){ 
			$firstDayPeriods = 4;
		}
		elseif (1 == $firstPeriodVal){
			$firstDayPeriods = 3;
		}
		elseif (2 == $firstPeriodVal){
			$firstDayPeriods = 2;
		}
		elseif (3 == $firstPeriodVal){
			$firstDayPeriods = 1;
		}
			
		if (0 == $numDays){
			return $firstDayPeriods;
		}
		elseif ($numDays >= 1 and $numDays <= 4){
			return ($numDays * 4) + $firstDayPeriods;
		}
		elseif ($numDays >= 5 and $numDays <=8){
			return  ($numDays - 4) + 16 + $firstDayPeriods;
		}
		else {
			throw new Exception("Invalid number of days provided. Valid number is >=0 and <=8");
		}
	}
	
	/**
	 * Returns the temperatures for each period
	 *
	 * @param boolean $rounded (default = False)
	 * @return array
	 */
	// returns $temperatures which contains temperatures for a consecutive numbers of days defined in $numDays
	public function getTemperatures($rounded = False){
		if ($this->numDays == ""){
			throw new Exception('Temperatures could not be retrieved. Either the XML-object is not initialized
								correctly, or $numDays variable is not defined');
		}
		else{
			for ($i = 0; $i < $this->numPeriods; $i++){
				
				if ($rounded) {
					$temperature[$i] = round($this->xml->forecast->tabular->time[$i]->temperature['value']);
				}
				else {
					$temperature[$i] = $this->xml->forecast->tabular->time[$i]->temperature['value'];	
				}
				
			}
			return $temperature;
		}
	}
	
	/**
	 * Defines the wind direction texts in an array in the following order:
	 * NORTH, NORTH-EAST, EAST TO NORTH-EAST, EAST, EAST TO SOUTH-EAST, SOUTH-EAST, SOUTH TO SOUTH-EAST,
	 * SOUTH, SOUTH TO SOUTH-WEST, SOUTH-WEST, WEST TO SOUTH-WEST, WEST, WEST TO NORTH-WEST, NORTH TO
	 * NORTH-WEST, NORTH
	 * 
	 * If you would like to override these values and use your own names, subclass this class and
	 * set the values accordingly.
	 *
	 */
	protected function setWindDirectionNames(){
		$this->windDirectionText = array("Nordlig", "Nord-øst", "Øst til nord-øst", "Østlig",
								"Øst til sør-øst", "Sør-øst", "Sør til sør-øst",
								"Sørlig", "Sør til sør-vest", "Sør-vest", "Vest til sør-vest",
								"Vestlig", "Vest til nord-vest", "Nord-vest", "Nord til nord-vest",
								"Nordlig");
	}
	/**
	 * Returns the spoken name of a wind direction defined in norwegian
	 *
	 * @param float $degree
	 * @return string
	 */
	public function getWindDirectionName($degree) {
		if ($degree >= 11.25 and $degree < 33.75) {
			return $this->windDirectionText[0];
		}
		elseif ($degree >= 33.75 and $degree < 56.25) {
			return $this->windDirectionText[1];
		}
		elseif ($degree >= 56.25 and $degree < 78.75) {
			return $this->windDirectionText[2];
		}
		elseif ($degree >= 78.75 and $degree < 101.25){
			return $this->windDirectionText[3];
		}
		elseif ($degree >= 101.25 and $degree < 123.75){
			return $this->windDirectionText[4];
		}	
		elseif ($degree >= 123.75 and $degree < 146.25) {
			return $this->windDirectionText[5];
		}
		elseif ($degree >= 146.25 and $degree < 168.75) {
			return $this->windDirectionText[6];
		}
		elseif ($degree >= 168.75 and $degree < 191.25) {
			return $this->windDirectionText[7];
		}
		elseif ($degree >= 191.25 and $degree < 213.75) {
			return $this->windDirectionText[8];
		}
		elseif ($degree >= 213.75 and $degree < 236.25){
			return $this->windDirectionText[9];
		}
		elseif ($degree >= 236.25 and $degree < 258.75) {
			return $this->windDirectionText[10];
		}
		elseif ($degree >= 258.75 and $degree < 281.25) {
			return $this->windDirectionText[11];
		}
		elseif ($degree >= 281.25 and $degree < 303.75) {
			return $this->windDirectionText[12];
		}
		elseif ($degree >= 303.75 and $degree < 326.25) {
			return $this->windDirectionText[13];
		}
		elseif ($degree >= 326.25 and $degree < 348.75) {
			return $this->windDirectionText[14];
		}
		else {
			return $this->windDirectionText[15];
		}
	}
	
	/**
	 * Returns the titles for text forecast (e.g. "Mandag til fredag"). 0-indexed == 0 for first period, 1 for second
	 *
	 * @return array
	 */
	public function getPeriodForecastTitles(){
		return array($this->xml->forecast->text->location->time[0]->title, 
					$this->xml->forecast->text->location->time[1]->title);
	}
	
	/**
	 * Returns the bodies for text forecast
	 *
	 * @return array	
	 */
	public function getPeriodForecastTexts(){
		return array($this->xml->forecast->text->location->time[0]->body,
				$this->xml->forecast->text->location->time[1]->body);
	}
	
	/**
	 * Returns the wind direction in degrees for each period
	 *
	 * @param boolean $rounded
	 * @return array
	 */
	public function getWindDirections($rounded = False) {
		if($this->numPeriods == ""){
			throw new Exception('Wind directions could not be retrieved since $numPeriods has no value');
		}
		else {
			for ($i = 0; $i < $this->numPeriods; $i++){	
				if ($rounded) {
					$windDirections[$i] = round($this->xml->forecast->tabular->time[$i]->windDirection['deg']);
				}
				else {
					$windDirections[$i] = $this->xml->forecast->tabular->time[$i]->windDirection['deg'];	
				}
			}
			return $windDirections;
		}
	}
	
	/**
	 * Returns the wind speed name for each period
	 *
	 * @return array
	 */
	public function getWindSpeedNames() {
		if($this->numPeriods == ""){
			throw new Exception('Windspeed names could not be retrieved since $numPeriods has no value');
		}
		else {
			for ($i = 0; $i < $this->numPeriods; $i++){	
				$windSpeedNames[$i] = $this->xml->forecast->tabular->time[$i]->windSpeed['name'];	
			}
		}
		return $windSpeedNames;
	}
	
	/**
	 * Returns the wind speed in meters per second for each period
	 *
	 * @param boolean $rounded
	 * @return array
	 */
	public function getWindSpeedMps($rounded = False) {
		if($this->numPeriods == ""){
			throw new Exception('Windspeed could not be retrieved since $numPeriods has no value');
		}
		else {
			for ($i = 0; $i < $this->numPeriods; $i++){	
				if ($rounded) {
					$windSpeedMps[$i] = round($this->xml->forecast->tabular->time[$i]->windSpeed['mps']);
				}
				else {
					$windSpeedMps[$i] = $this->xml->forecast->tabular->time[$i]->windSpeed['mps'];	
				}
			}
			return $windSpeedMps;
		}
	}
	
	/**
	 * Returns the precipitation for each period
	 *
	 * @param boolean $rounded
	 * @return array
	 */
	public function getPrecipitations($rounded = False) {
		if($this->numPeriods == ""){
			throw new Exception('Precipitations could not be retrieved since $numPeriods has no value');
		}
		else {	
			for ($i = 0; $i < $this->numPeriods; $i++){	
				if ($rounded) {
					$precipitations[$i] = round($this->xml->forecast->tabular->time[$i]->precipitation['value']);
				}
				else {
					$precipitations[$i] = $this->xml->forecast->tabular->time[$i]->precipitation['value'];
				}
			}
			return $precipitations;
		}
	}

	/**
	 * Returns the symbol number for each period
	 *
	 * @return array
	 */
	public function getSymbolNumbers() {
		if($this->numPeriods == ""){
			throw new Exception('Symbol numbers could not be retrieved since $numPeriods has no value');
		}
		else {
			for ($i = 0; $i < $this->numPeriods; $i++){	
				$symbolNumbers[$i] = $this->xml->forecast->tabular->time[$i]->symbol['number'];	
			}
			return $symbolNumbers;
		}
	}
	
	/**
	 * Returns the symbol name for each period
	 *
	 * @return array
	 */
	public function getSymbolNames() {
		if($this->numPeriods == ""){
			throw new Exception('Symbol names could not be retrieved since $numPeriods has no value');
		}
		else {
			for ($i = 0; $i < $this->numPeriods; $i++){	
				$symbolNames[$i] = $this->xml->forecast->tabular->time[$i]->symbol['name'];
			}
			return $symbolNames;
		}
	}
}
?>